#include "stdafx.h"
#include "game/gamemgr.h"
#include "./tankobj.h"

DEF_MODEL_NAME(TankObj, "tank")

#define TANKROLL "tankroll"
#define TANKBODY "tankbody"

#define TANK_INVINCIBLE_TIME 0.1
#define TANK_IMPACTED_TIME 1.

#define MAX_TANK_SPEED 50
#define MAX_TANK_ROLLANGLESPEED 250
#define MAX_TANK_GUNANGLESPEED 500

#define FULLRADIAN 360 * MathUtils::AngleToRadian_MagicNum

#define TANK_RANGE 1.5
#define TANK_IMPACTABLE true

Point TankObj::s_texOffset(0, MAX_TANK_SPEED);

TankObj::TankObj(int id, Point startPos, float rollRadian, float gunRadian, Color color)
: ImpactObj(id, TANK_IMPACTABLE)
, m_bInvincible(false)
, m_bGo(false)
, m_bAhead(true)
, m_bInivicibleVisible(true)
, m_bImpacted(false)
, m_impactedTime(0)
, m_invincibleTime(0)
, m_dstRollRadian(0)
, m_dstGunRadian(0)
, m_curRollRadian(0)
, m_curGunRadian(0)
, m_speed(MAX_TANK_SPEED)
, m_rollspeed(MAX_TANK_ROLLANGLESPEED)
, m_gunspeed(MAX_TANK_GUNANGLESPEED)
{
	memcpy(&m_pos, &startPos, sizeof(Point));
	m_curRollRadian = rollRadian;
	m_dstRollRadian = rollRadian;
	m_curGunRadian = gunRadian;
	m_dstGunRadian = gunRadian;

	AnimationHelper* aniHelper = m_model.GetAnimationHelper();

	aniHelper->ChooseModel(TANKROLL);
	aniHelper->MoveToPos(startPos);
	aniHelper->TurnAngle(rollRadian);

	aniHelper->ChooseModel(TANKBODY);
	aniHelper->MoveToPos(startPos);
	aniHelper->TurnAngle(gunRadian);
	aniHelper->SetColor(color);
}

TankObj::~TankObj()
{
}

void TankObj::OnRender(float differTime)
{
	if (!m_bVisible)
		return;

	CalcRender(differTime);
	CalcLimitPos();

	if (m_bGo)
	{
		CalcTankImpact();
	}

	if (m_bInivicibleVisible)
	{
		m_model.Render();
	}
}

void TankObj::TurnRollToRadian(float dstRadian)
{
	m_dstRollRadian = dstRadian;
}

void TankObj::ReverseRollRadian(bool bPositive)
{
	if (bPositive)
	{
		if (m_curRollRadian <= 0)
		{
			m_curRollRadian += FULLRADIAN;
		}
	}
	else
	{
		if (m_curRollRadian >= 0)
		{
			m_curRollRadian -= FULLRADIAN;
		}
	}
}

void TankObj::TurnGunToRadian(float dstRadian)
{
	m_dstGunRadian = dstRadian;
}

void TankObj::ReverseGunRadian(bool bPositive)
{
	if (bPositive)
	{
		if (m_curGunRadian <= 0)
		{
			m_curGunRadian += FULLRADIAN;
		}		
	}
	else
	{
		if (m_curGunRadian >= 0)
		{
			m_curGunRadian -= FULLRADIAN;
		}
	}
}

void TankObj::Go(bool bGo)
{
	m_bGo = bGo;
}

void TankObj::SetGoHeading(bool bAhead)
{
	m_bAhead = bAhead;
}

float TankObj::GetRollRadian()
{
	return m_curRollRadian;
}

float TankObj::GetGunRadian()
{
	return m_curGunRadian;
}

bool TankObj::GetHeading()
{
	return m_bAhead;
}

bool TankObj::GetGo()
{
	return m_bGo;
}

Point TankObj::GetPos()
{
	return m_pos;
}

float TankObj::GetHeadingRadian()
{
	return m_bAhead ? m_curRollRadian : MathUtils::PI - m_curRollRadian;
}

float TankObj::GetRange()
{
	return TANK_RANGE;
}

bool TankObj::Impacted()
{
	if (m_bImpacted)
	{
		return false;
	}

	m_bImpacted = true;
	m_bInvincible = true;
	m_bInivicibleVisible = false;

	FireEvent(OnTankModelImpactedFunc, OnImpacted, ());
	return true;
}

float TankObj::GetSpeed()
{
	return m_speed;
}

float TankObj::GetRollSpeed()
{
	return m_rollspeed;
}

float TankObj::GetGunSpeed()
{
	return m_gunspeed;
}

void TankObj::SetInvincible(bool bInvincible)
{
	//m_bInvincible = bInvincible;
}

void TankObj::SetSpeed(float speed)
{
	if (speed > MAX_TANK_SPEED)
	{
		speed = MAX_TANK_SPEED;
	}
	m_speed = speed;
}

void TankObj::SetRollSpeed(float rollspeed)
{
	if (rollspeed > MAX_TANK_ROLLANGLESPEED)
	{
		rollspeed = MAX_TANK_ROLLANGLESPEED;
	}
	m_rollspeed = rollspeed;
}

void TankObj::SetGunSpeed(float gunspeed)
{
	if (gunspeed > MAX_TANK_GUNANGLESPEED)
	{
		gunspeed = MAX_TANK_GUNANGLESPEED;
	}
	m_gunspeed = gunspeed;
}

void TankObj::CalcRender(float differTime)
{
	float tankDistance = 0.;

	//tank roll
	m_model.GetAnimationHelper()->ChooseModel(TANKROLL);

	float differRollAngle = CalcDifferRadian(MathUtils::AngleToRadian(m_rollspeed) * differTime, m_dstRollRadian, m_curRollRadian);
	m_curRollRadian += differRollAngle;
	m_model.GetAnimationHelper()->TurnAngle(differRollAngle);

	if (m_bGo)
	{
		tankDistance = m_bAhead ? m_speed * differTime : -m_speed * differTime;

		m_model.GetAnimationHelper()->MoveOffset(m_curRollRadian, tankDistance, &m_pos);
		m_model.GetAnimationHelper()->TextureOffset(s_texOffset);
	}

	//Tank body
	m_model.GetAnimationHelper()->ChooseModel(TANKBODY);

	float differGunAngle = CalcDifferRadian(MathUtils::AngleToRadian(m_gunspeed) * differTime, m_dstGunRadian, m_curGunRadian);
	m_curGunRadian += differGunAngle;
	m_model.GetAnimationHelper()->TurnAngle(differGunAngle);

	if (m_bGo)
	{
		m_model.GetAnimationHelper()->MoveOffset(m_curRollRadian, tankDistance);
	}

	if (m_bInvincible)
	{
		m_invincibleTime += differTime;

		if (m_invincibleTime > TANK_INVINCIBLE_TIME)
		{
			m_invincibleTime = 0.;
			m_bInivicibleVisible = !m_bInivicibleVisible;
		}
	}

	if (m_bImpacted)
	{
		m_impactedTime += differTime;
		if (m_impactedTime > TANK_IMPACTED_TIME)
		{
			m_impactedTime = 0;
			m_invincibleTime = 0;
			m_bInvincible = false;
			m_bImpacted = false;
			m_bInivicibleVisible = true;
		}
	}
}

void TankObj::CalcLimitPos()
{
	Size stageSize = GameMgr::GetInstance().GetStageSize();
	bool bLimit = false;

	if (m_pos.x - TANK_RANGE < 0)
	{
		m_pos.x = TANK_RANGE;
		bLimit = true;
	}

	if (m_pos.y - TANK_RANGE < 0)
	{
		m_pos.y = TANK_RANGE;
		bLimit = true;
	}

	if (m_pos.x + TANK_RANGE > stageSize.cx)
	{
		m_pos.x = stageSize.cx - TANK_RANGE;
		bLimit = true;
	}

	if (m_pos.y + TANK_RANGE > stageSize.cy)
	{
		m_pos.y = stageSize.cy - TANK_RANGE;
		bLimit = true;
	}

	if (bLimit)
	{
		AnimationHelper* aniHelper = m_model.GetAnimationHelper();

		aniHelper->ChooseModel(TANKROLL);
		aniHelper->MoveToPos(m_pos);
		aniHelper->ChooseModel(TANKBODY);
		aniHelper->MoveToPos(m_pos);
	}
}

void TankObj::CalcTankImpact()
{
	if (CalcImpact(m_pos))
	{
		AnimationHelper* aniHelper = m_model.GetAnimationHelper();

		aniHelper->ChooseModel(TANKROLL);
		aniHelper->MoveToPos(m_pos);
		aniHelper->ChooseModel(TANKBODY);
		aniHelper->MoveToPos(m_pos);
	}
}

float TankObj::CalcDifferRadian(float differObjRadian, float dstRadian, float curRadian)
{
	float differRadian = dstRadian - curRadian;

	if (dstRadian >= curRadian)
	{
		if (differRadian < differObjRadian)
		{
			differObjRadian = differRadian;
		}
	}
	else
	{
		differObjRadian = -differObjRadian;
		if (differRadian > differObjRadian)
		{
			differObjRadian = differRadian;
		}
	}
	return differObjRadian;
}